package database

import (
	"github.com/google/wire"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	_ "github.com/jinzhu/gorm/dialects/postgres"
	"github.com/pkg/errors"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"time"
)

// Options database option
type Options struct {
	Mysql struct {
		URL   string `yaml:"url"`
		Debug bool
	}
	Postgres struct {
		Source  string
		Debug   bool
		MaxConn int
	}
}

// NewOptions new database option
func NewOptions(v *viper.Viper, logger *zap.Logger) (*Options, error) {
	var (
		err error
		o   = new(Options)
	)
	if err = v.UnmarshalKey("database", o); err != nil {
		return nil, errors.Wrap(err, "unmarshal database option error")
	}

	logger.Info("load database options success", zap.Any("database options", o))
	return o, err
}

type Database struct {
	Mysql    *gorm.DB
	Postgres *gorm.DB
}

// DBClient  mysql连接类型
var DBClient Database

// New new database
func New(o *Options) (*Database, error) {
	var d = new(Database)
	if o.Mysql.URL == "" && o.Postgres.Source == "" {
		return nil, errors.New("缺少mysql和postgres配置")
	}
	if o.Mysql.URL != "" {
		mysql, err := mysql(o)
		if err != nil {
			return nil, err
		}
		d.Mysql = mysql
	}
	if o.Postgres.Source != "" {
		postgres, err := postgres(o)
		if err != nil {
			return nil, err
		}
		d.Postgres = postgres
	}
	DBClient.Mysql = d.Mysql
	return d, nil
}

func mysql(o *Options) (*gorm.DB, error) {
	db, err := gorm.Open("mysql", o.Mysql.URL)
	if err != nil {
		return nil, errors.Wrap(err, "gorm open mysql connection error")
	}
	err = db.DB().Ping()
	if err != nil {
		return nil, errors.Wrap(err, "mysql ping fail")
	}
	if o.Mysql.Debug {
		db = db.Debug()
	}
	db.DB().SetConnMaxLifetime(time.Minute * 10)
	return db, nil
}

func postgres(o *Options) (*gorm.DB, error) {
	db, err := gorm.Open("postgres", o.Postgres.Source)
	if err != nil {
		return nil, errors.Wrap(err, "gorm open mysql connection error")
	}
	err = db.DB().Ping()
	if err != nil {
		return nil, errors.Wrap(err, "mysql ping fail")
	}
	if o.Postgres.Debug {
		db = db.Debug()
	}
	db.DB().SetMaxIdleConns(o.Postgres.MaxConn)
	return db, nil
}

// ProviderSet dependency injection
var ProviderSet = wire.NewSet(New, NewOptions)
